# -*- coding: utf-8 -*-
# @Time    : 2025/10/5
# @Author  : 楚江涛
# @FileName: errors.py
# @Software: PyCharm


"""
app/domain/errors.py
--------------------
定义工作流系统中的领域级异常（Domain Exceptions）。

这些异常用于：
1. 表达系统或业务逻辑错误；
2. 在 API 层中统一捕获并转换为标准 JSON 响应；
3. 方便未来的国际化与统一日志记录。

规范：
- 所有异常均继承自 WorkflowBaseError；
- 每个异常包含 code（错误标识）、message（中文提示）、details（附加信息）；
- 统一提供 to_dict() 方法便于序列化。
"""

from typing import Optional, Any, Dict


class WorkflowBaseError(Exception):
    """工作流系统异常基类"""

    code: str = "workflow_error"
    message: str = "工作流系统发生未知错误。"

    def __init__(self, message: Optional[str] = None, details: Optional[Dict[str, Any]] = None):
        super().__init__(message or self.message)
        self.details = details or {}

    def to_dict(self) -> Dict[str, Any]:
        """转为统一的 JSON 响应格式"""
        return {
            "error": {
                "code": self.code,
                "message": str(self),
                "details": self.details,
            }
        }


# ============================================================
# 一、工作流定义与结构错误
# ============================================================

class InvalidWorkflow(WorkflowBaseError):
    """工作流定义非法或缺失必要字段"""
    code = "invalid_workflow"
    message = "工作流定义不合法，请检查节点和连线结构。"


class NodeNotFound(WorkflowBaseError):
    """指定的节点在工作流中不存在"""
    code = "node_not_found"
    message = "未找到指定的节点，请确认节点 ID 是否正确。"


class DuplicateNodeId(WorkflowBaseError):
    """存在重复的节点 ID"""
    code = "duplicate_node_id"
    message = "工作流中存在重复的节点 ID，请修改后重试。"


class InvalidEdgeReference(WorkflowBaseError):
    """边的 source 或 target 节点引用错误"""
    code = "invalid_edge_reference"
    message = "工作流连线引用了不存在的节点，请检查边定义。"


# ============================================================
# 二、执行阶段错误
# ============================================================

class ExecNotFound(WorkflowBaseError):
    """执行实例不存在或已结束"""
    code = "execution_not_found"
    message = "未找到对应的执行实例或该任务已结束。"


class InvalidExecState(WorkflowBaseError):
    """执行状态与操作不匹配"""
    code = "invalid_execution_state"
    message = "当前执行状态不允许执行该操作。"


class NodeExecutionError(WorkflowBaseError):
    """节点执行时发生未知错误"""
    code = "node_execution_error"
    message = "节点执行过程中发生错误，请检查节点逻辑。"


class NodeTimeoutError(NodeExecutionError):
    """节点执行超时"""
    code = "node_timeout"
    message = "节点执行超时，请检查代码逻辑或调整超时时间。"


class NodeCancelled(WorkflowBaseError):
    """节点被取消"""
    code = "node_cancelled"
    message = "节点执行已被取消。"


class InvalidCodeNode(WorkflowBaseError):
    """代码节点定义或执行错误"""
    code = "invalid_code_node"
    message = "代码节点定义不规范或执行出错，请检查代码格式与函数签名。"


# ============================================================
# 三、控制与权限错误
# ============================================================

class PermissionDenied(WorkflowBaseError):
    """无权执行操作"""
    code = "permission_denied"
    message = "您没有权限执行该操作。"


class OperationConflict(WorkflowBaseError):
    """操作冲突（如重复取消或重复启动）"""
    code = "operation_conflict"
    message = "当前操作与系统状态冲突，请稍后重试。"


# ============================================================
# 四、外部依赖与环境错误
# ============================================================

class ExternalServiceError(WorkflowBaseError):
    """外部服务（Redis、数据库、子进程等）异常"""
    code = "external_service_error"
    message = "外部服务调用失败，请稍后重试或联系管理员。"


class CodeRunnerError(WorkflowBaseError):
    """代码执行器异常"""
    code = "code_runner_error"
    message = "代码执行器运行出错，请检查执行环境。"


# ============================================================
# 五、通用工具函数
# ============================================================

def handle_exception(exc: Exception) -> Dict[str, Any]:
    """
    统一异常处理函数。
    将系统异常包装为统一的 JSON 响应。
    """
    if isinstance(exc, WorkflowBaseError):
        return exc.to_dict()
    else:
        return WorkflowBaseError(f"系统错误：{exc}").to_dict()

